#!/usr/bin/perl

#require "./user_admin.pl"; #user_admin sub functions
use FindBin qw($Bin $Script);
use Cwd qw(getcwd abs_path);
$Bin = abs_path($Bin);

require Tk::LabEntry;
require Tk::DialogBox;
use Tk;

use IO::Socket::INET;
use Digest::SHA qw(sha256_hex);
#use Digest::MD5 qw(md5_hex);
use Time::HiRes qw(gettimeofday);

my $commands_file = "commands.txt";
my $results_file = "results.txt";

my $host = "127.0.0.1";
my $st_port = 5000;
my $mx_port = 5024;
my $pub_port = 5911;

my $resetpw = "admin";
my $pub_resetpw = "public";

my $neighbor_max = 24;
my $guest_max_tm = 99999;
my $temperature_max = 9999;
my $unknown = "UNK";

my $arm_status;
my %portHashSensor;
my %portHashNeighbor;
armMsg(-1);

############################################################################
my $window = MainWindow->new;
$window->geometry("600x500");
$window->title("Alarm Command Terminal");
my $menu = $window->Menu();
$window->configure(-menu => $menu);

my $User = $menu->Menubutton(-text => "User", -underline=> 0, -tearoff=>0);
$User->command(-label => "Change my Password", -command => \&ChangeMyPass);
$User->command(-label => "Add a User", -command => \&AddUser);
$User->command(-label => "Delete a User", -command => \&DelUser);
$User->command(-label => "Rename a User", -command => \&Rename);
$User->command(-label => "Change a User's Password", -command => \&ChangePass);
$User->command(-label => "Disable a User", -command => \&Disable);
$User->command(-label => "Enable a User", -command => \&Enable);
$User->command(-label => "Set a User's Type", -command => \&SetType);
$User->command(-label => "Show List of Users", -command => \&ListUser);

my $Sensor = $menu->Menubutton(-text => "Sensor", -underline=> 0, -tearoff=>0);
$Sensor->command(-label => "Edit a Sensor", -command => \&EditSensor);
# $Sensor->command(-label => "Remove a Sensor", -command => \&RemoveSensor);
$Sensor->command(-label => "Enable/Disable a Sensor", -command => \&EnableSensor);
$Sensor->command(-label => "List Sensors", -command => \&ListSensor);

my $Neighbor = $menu->Menubutton(-text => "Neighbor", -underline=> 0, -tearoff=>0);
$Neighbor->command(-label => "Add a Neighbor", -command => \&AddNeighbor);
$Neighbor->command(-label => "Remove a Neighbor", -command => \&RemoveNeighbor);
$Neighbor->command(-label => "Enable/Disable a Neighbor", -command => \&EnableNeighbor);
$Neighbor->command(-label => "List Neighbors", -command => \&ListNeighbor);

my $Health = $menu->Menubutton(-text => "System", -underline => 1, -tearoff=>0);
$Health->command(-label => "Show Arm Status", -command => \&ArmStatus);
$Health->command(-label => "Arm System", -command => \&ArmSystem);
$Health->command(-label => "Disarm System", -command => \&DisarmSystem);
$Health->command(-label => "Show System Health", -command => \&SystemHealth);

my $Home = $menu->command(-label => "Home", -underline => 0, -command => \&HomePage);
my $Quit = $menu->command(-label => "Quit", -underline => 0, -command => \&fin);

$window->Label(-text => "Welcome to the Command Terminal")->pack(-anchor => "n");
$window->Label(-textvariable => \$answer, -justify => "left")->pack(-anchor => "w");
$window->gridColumnconfigure(0, -minsize => 300);

$db = $window->DialogBox(-title => 'Login', -buttons => ['Ok', 'Cancel'], -default_button => 'Ok');
$db->add('LabEntry', -textvariable => \$cuser, -width =>20, -label => 'Username', -labelPack => [-side => 'left'])->pack;
$db->add('LabEntry', -textvariable => \$cpass, -width =>20, -label => 'Password', -show=> '*', -labelPack => [-side => 'left'])->pack;

$result = $db->Show();

#Sanitizing User Input
$bad_chars = '\s\\\/';
if (length($cuser) > 19) {
	print "CON: Username ".$cuser . " is too long\n";
	fin();
}
elsif (length($cpass) > 19) {
	print "CON: Password ".$cpass . " is too long\n";
	fin();
}
elsif ($cuser =~ /[$bad_chars]/){
	print "CON: Username ".$cuser." contains unacceptable characters\n";
	fin();
}
elsif ($cpass =~ /[$bad_chars]/){
	print "CON: Password ".$cpass." contains unacceptable characters\n";
	fin();
}elsif ($result eq "Ok") {
	$usertype = system("echo \"auth\n$cuser\n$cpass\" | /usr/bin/perl $Bin/user_admin.pl stdin") >> 8;
	if($usertype == 8){
		$usertype = "Administrator";
	}
	elsif($usertype == 9){
		$usertype = "Control";
		$menu->delete(0,1);
		my $ChangeP = $menu->command(-label => "Password", -underline => 0, -command => \&ChangeMyPass);
	}
	elsif($usertype == 10){
		$usertype = "Monitor";
		$menu->delete(0,3);
		my $ChangeP = $menu->command(-label => "Password", -underline => 0, -command => \&ChangeMyPass);
	}
	elsif($usertype == 12){
		$usertype = "Guest";
		$menu->delete(0,3);
		my $ChangeP = $menu->command(-label => "Password", -underline => 0, -command => \&ChangeMyPass);
	}
	else{
		#Either bad password, user is disabled, or no such user.  Error message will be printed by user_admin.pl system call.
		fin();
		exit 1;
	}
	$window->title("Alarm Command Terminal ($cuser - $usertype)");
	HomePage();
}
else{
	fin();
}

MainLoop;

print "CON: Run steps to cleanup\n";
#killProcD("sensor_d.pl");
exit 0;

################################

sub HomePage {
	$answer  = "\n    Hi $cuser,\n\n";
	$answer .= "      You have the \"$usertype\" permission.\n";
	$answer .= "\n";
	$answer .= "      The Simulator:\n";
	$answer .= "           - Each sensor has a sensor simulator daemon running after the system\n";
	$answer .= "             is armed\n";
	$answer .= "           - The sensor simulator TCP listening ports are starting from \"$st_port\"\n";
	$answer .= "           - The sensor simulator crypto passwd and reset_pw are \"$resetpw\"\n";
	$answer .= "           - The alarm  simulator TCP listening port is \"$pub_port\"\n";
	$answer .= "           - The alarm simulator crypto passwd and reset_pw are \"$pub_resetpw\"\n";
	$answer .= "\n";
	$answer .= "      Tips:\n";
	$answer .= "           - Suggest to disable the neighabor if its IP address is unreachable.\n";
	$answer .= "             This helps to reduce the pause time when checking for system health.\n";
	$answer .= "           - If you receive \"Health: $unknown\"  while checking for system health,\n";
	$answer .= "             it is likely because the system hasn't been armed.\n";
	$answer .= "           - For simplicity, this release does not allow for spaces in a name\n";
	$answer .= "             string\n"; 
	$answer .= "\n\n";
	$answer .= "     Enjoy!\n";
}

sub validateInput {
	if (length($_[0]) <= 0) {
		$answer = "\nEnmpty field unacceptable\n";
		return 0;
	}

	if (length($_[0]) > 19) {
		$answer = "Input \"".$_[0] . "\" is too long\n";
		return 0;
	} elsif ($_[0] =~ /[$bad_chars]/) {
		$answer = "Input \"".$_[0]."\" contains unacceptable characters\n";
		return 0;
	}
	return 1;
}

sub validateInputNum {
	if (length($_[0]) > 19) {
		$answer = "Input \"".$_[0] . "\" is too long\n";
		return 0;
	}

	my $val = $_[0];
	$val =~ s/[0123456789]//g;   #remove all digits 
	if ((length($val) != 0) || ($_[0] > $_[1])) {
		$answer = "Input \"".$_[0]."\" is not acceptable. The valid range is from 0 to $_[1].\n";
		return 0;
	}
	return 1;
}

sub readConsoleData {
	my $ctrl_data = "$Bin/consoledata.udb";
	if (open(FH, $ctrl_data)) {
		my $mode = "";
		my $i = -1;
		my $j = -1;
		while (<FH>) {
			chomp($_);
			$_ =~ s/^( +)//; #remove spaces in front
			if ($_ =~ /\[Sensor/) {
				$i += 1;
				$mode = "Sensor";
			}
			elsif ($_ =~ /\[Neighbor/) {
				$j += 1;
				$mode = "Neighbor";
			}
			else {
				if ($mode eq "Sensor") {
					my($role, $value) = split(/=/, $_);
					$sensorlist[$i]{$role} = $value;
				}
				elsif ($mode eq "Neighbor") {
					my($role, $value) = split(/=/, $_);
					$neighborlist[$j]{$role} = $value;
				}
			}
		}
		$#sensorlist = $i;
		$#neighborlist = $j;
		close(FH);
	}
	else {
		$#sensorlist = -1;
		$#neighborlist = -1;
		return 0;
	}
	return 1;
}

sub writeConsoleData {
	my $ctrl_data = "$Bin/consoledata.udb";
	if (open(FH, ">$ctrl_data")) {
		for $i(0 .. $#sensorlist) {
			print(FH "[Sensor]\n");
			for $role (keys %{$sensorlist[$i]}){
				print(FH "$role=$sensorlist[$i]{$role}\n");
			}
		}
		for $i(0 .. $#neighborlist) {
			print(FH "[Neighbor]\n");
			for $role (keys %{$neighborlist[$i]}){
				print(FH "$role=$neighborlist[$i]{$role}\n");
			}
		}
		close(FH);
	}
	else {
		$answer = "Unable to save the data to file\n\"$sensor_data\"\n";
		return 0;
	}
	return 1;
}

sub EditSensor{
	my $loc_port = $st_port;

	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("300x100");
	$subwin1->title("Select a Port");

	my $m = $mx_port - 1;;
	$subwin1->Label(-text => "Port (range: $st_port - $m)")->pack();
	$subwin1->Entry(-textvariable => \$loc_port)->pack();

	$subwin1->Button(-text=> "OK", -command => sub{EditSensorDo($loc_port); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}

sub EditSensorDo {
	my($loc_port) = @_;

	if (($loc_port < $st_port) || ($loc_port >= $mx_port))  {
		my $m = $mx_port - 1;
		$answer = "\nSorry, not a valid port. The valid range is between $st_port to $m.\n";
		return;
	}

	my $i= $loc_port - $st_port;
	$name = $sensorlist[$i]{Name};
	$sensor = $sensorlist[$i]{Type};
	$t_value = $sensorlist[$i]{SetVal};
	$confirm = 0;
	if ($sensorlist[$i]{Mode} eq "Enable") {
		$confirm = 1;
	}

	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("300x250");
	$subwin1->title("Edit a Sensor");

	$subwin1->Label(-text => "Name")->pack();
	$subwin1->Entry(-textvariable => \$name)->pack();

	foreach (qw/Window Door Motion Temperature/){
		$subwin1->Radiobutton(-text => $_, -variable => \$sensor, -value=> $_)->pack(-anchor => 'w');
	}

	$subwin1->Label(-text => "Temperature Threshold (Fahrenheit)")->pack();
	$subwin1->Entry(-textvariable => \$t_value)->pack();

	$subwin1->Checkbutton(-text => "Confirm Enable", -variable => \$confirm)->pack(-anchor => 'w');

	$subwin1->Button(-text=> "OK", -command => sub{AddSensorWork($loc_port); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}

sub AddSensorWork{
	my($loc_port) = @_;

	my $flag = 0;

	if (!validateInput($name) || (length($sensor) <= 0)) {
		$answer = "\nSorry, not a valid sensor\n";
		$name = "";
		return;
	}

	if ($sensor eq "Temperature") {
		$sensor = "Temp.";
		if (length($t_value) <= 0) {
			$t_value = 150;   #default value
		}
		$t_value =~ s/^( +)//;  #remove all spaces in front
		if (validateInputNum($t_value, $temperature_max)) {
			$answer = "The ". $sensor . " Sensor called \"" . $name . "\" is ready.\nTemperature is set at " . $t_value . " degrees Fahrenheit.";
			$flag = 1;
		}
		else {
			$answer = "\nSorry, not a valid sensor\n";
		}
	}
	else {
		$t_value = "";
		$answer = "The ". $sensor . " Sensor called \"" . $name . "\" is ready.";
		$flag = 1;
	}

	if ($flag == 1) {
		my $mode = "Enable";
		if ($confirm eq "0") {
			$mode = "Disable";
		}
		$answer = $answer . " Mode is \"" . $mode . "\"\n";

		my $i= $loc_port - $st_port;
		$sensorlist[$i]{Name} = $name;
		$sensorlist[$i]{Mode} = $mode;
		$sensorlist[$i]{Type} = $sensor;
		$sensorlist[$i]{SetVal} = $t_value;

		writeConsoleData();
		killProcD("sensor_d.pl");
		$answer = $answer . "\n****** Please re-ARM the system ******\n";
	}
	$name = "";
}

sub ChangeMyPass{
	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("250x200");
	$subwin1->title("Change My Password");
	$subwin1->Label(-text => "Old Password")->pack();
	$subwin1->Entry(-textvariable => \$oldpass, -show=>'*')->pack();
	$subwin1->Label(-text => "New Password")->pack();
	$subwin1->Entry(-textvariable => \$pass, -show=>'*')->pack();
	$subwin1->Label(-text => "Confirm Password")->pack();
	$subwin1->Entry(-textvariable => \$pass2, -show=>'*')->pack();
	$subwin1->Button(-text=> "OK", -command => sub{ChangeMyPassWork(); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}

sub ChangeMyPassWork{
	if (validateInput($pass) && validateInput($pass2)) {
		if ((length($pass) > 0) && ($pass eq $pass2)) {
			$userwork = "chgmypass $oldpass $pass $pass2\n";
			SetCommands("NEWPW");
		}
		else {
			$answer = "Passwords don't match";
		}
	}
}

sub AddUser{
	$name = "";
	$user = "";
	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("250x320");
	$subwin1->title("Add a User");
	$subwin1->Label(-text => "Name")->pack();
	$subwin1->Entry(-textvariable => \$name)->pack();
	$subwin1->Label(-text => "Password")->pack();
	$subwin1->Entry(-textvariable => \$pass, -show=>'*')->pack();
	$subwin1->Label(-text => "Confirm Password")->pack();
	$subwin1->Entry(-textvariable => \$pass2, -show=>'*')->pack();
	foreach (qw/Admin Control Monitor Guest/){
		$subwin1->Radiobutton(-text => $_, -variable => \$user, -value=> $_)->pack(-anchor => 'w');
	}

	$subwin1->Label(-text => "Minutes to be Valid")->pack();
	$v_value = "";
	$subwin1->Entry(-textvariable => \$v_value)->pack();

	$subwin1->Button(-text=> "OK", -command => sub{AddUserWork(); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}

sub AddUserWork{
	if (validateInput($name) && validateInput($pass)) {
 		if ($pass == $pass2) {
			my $flag = 1;
			$type = substr($user, 0, 1);
			if ($type eq "G") {
				if (length($v_value) <= 0) {
					$v_value = 60;   #default value
				}
				$v_value =~ s/^( +)//;  #remove all spaces in front
				if (!validateInputNum($v_value, $guest_max_tm)) {
					$answer = "\nSorry, not a valid guest time limit\n";
					$flag = 0;
				}
				else {
					$type .= " $v_value";
				}
			}
			if ($flag) {
				$userwork = "adduser $name $pass $pass2 $type\n";
				SetCommands("");
			}
		}
		else {
			$answer = "Passwords don't match";
		}
	}
}

sub DelUser{
	$name = "";
	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("250x110");
	$subwin1->title("Delete User");
	$subwin1->Label(-text => "Name")->pack();
	$subwin1->Entry(-textvariable => \$name)->pack();
	$confirm = 'N';
	foreach (qw/Confirm?/){
		$subwin1->Radiobutton(-text => $_, -variable => \$confirm, -value=> $_)->pack(-anchor => 'w');
	}
	$subwin1->Button(-text=> "OK", -command => sub{DelUserWork(); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}
##//
sub DelUserWork{
	if(validateInput($name)){
		if ($confirm ne 'N'){
			$confirm = 'Y';
		}
		genCommFiles();
		open (CFILE, ">$commands_file");
		print CFILE "deluser $name\n";
		print CFILE "$confirm\n";
		print CFILE "exit";
		close (CFILE);
		UserAdminWork($name, "");
	}
}

sub Rename{
	$name = "";
	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("250x130");
	$subwin1->title("Rename a User");
	$subwin1->Label(-text => "Current Name")->pack();
	$subwin1->Entry(-textvariable => \$name)->pack();
	$subwin1->Label(-text => "New Name")->pack();
	$subwin1->Entry(-textvariable => \$newname)->pack();
	$subwin1->Button(-text=> "OK", -command => sub{RenameWork(); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}

sub RenameWork{
	if(validateInput($name) && validateInput($newname)){
	$userwork = "rename $name $newname\n";
	SetCommands("");
	}
}

sub ChangePass{
	$name = "";
	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("250x180");
	$subwin1->title("Change a User's Password");
	$subwin1->Label(-text => "Name")->pack();
	$subwin1->Entry(-textvariable => \$name)->pack();
	$subwin1->Label(-text => "New Password")->pack();
	$subwin1->Entry(-textvariable => \$pass, -show=>'*')->pack();
	$subwin1->Label(-text => "Confirm Password")->pack();
	$subwin1->Entry(-textvariable => \$pass2, -show=>'*')->pack();
	$subwin1->Button(-text=> "OK", -command => sub{ChangePassWork(); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}

sub ChangePassWork{
	if(validateInput($name) && validateInput($pass) && validateInput($pass2)){
		if($pass == $pass2){
			$userwork = "chgpass $name $pass $pass2\n";
			SetCommands("");
		}else{
			$answer = "Passwords don't match";
		}
	}
}

sub Disable{
	$name = "";
	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("250x90");
	$subwin1->title("Disable a User");
	$subwin1->Label(-text => "Name")->pack();
	$subwin1->Entry(-textvariable => \$name)->pack(); 
	$subwin1->Button(-text=> "OK", -command => sub{DisableWork(); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}

sub DisableWork{
	if(validateInput($name)){
		$userwork = "disuser $name\n";
		SetCommands("");
	}	
}

sub Enable{
	$name = "";
	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("250x90");
	$subwin1->title("Enable a User");
	$subwin1->Label(-text => "Name")->pack();
	$subwin1->Entry(-textvariable => \$name)->pack();
	$subwin1->Button(-text=> "OK", -command => sub{EnableWork(); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}

sub EnableWork{
	if(validateInput($name)){
		$userwork = "enuser $name\n";
		SetCommands("");
	}
}

sub SetType{
	$name = "";
	my $subwin1 = $window->Toplevel;
	#$subwin1->geometry("250x250");
	$subwin1->geometry("250x180");
	$subwin1->title("Set a User's Type");
	$subwin1->Label(-text => "Name")->pack();
	$subwin1->Entry(-textvariable => \$name)->pack();
	foreach (qw/Admin Control Monitor Guest/) {
		my $m = $_;
		$user = "";
		if ($m eq "Guest") { $m .= " (Valid for 60 minutes)"; }
		$subwin1->Radiobutton(-text => $m, -variable => \$user, -value=> $_)->pack(-anchor => 'w');
	}

	#$v_value = "";
	#$subwin1->Label(-text => "Minutes to be Valid")->pack();
	#$subwin1->Entry(-textvariable => \$v_value)->pack();

	$subwin1->Button(-text=> "OK", -command => sub{SetTypeWork(); $subwin1->destroy if Tk::Exists($subwin1);})->pack();	
}

sub SetTypeWork{
	$type = substr($user, 0, 1);
	if (validateInput($name)) {
		my $flag = 1;
		#if ($type eq "G") {
		#	if (length($v_value) <= 0) {
		#		$v_value = 60;   #default value
		#	}
		#	$v_value =~ s/^( +)//;  #remove all spaces in front
		#	if (!validateInputNum($v_value, $guest_max_tm)) {
		#		$answer = "\nSorry, not a valid guest time limit\n";
		#		$flag = 0;
		#	}
		#	else {
		#		$type .= " $v_value";
		#	}
		#}
		if ($flag) {
			$userwork = "settype $name $type\n";
			SetCommands("");
		}
	}
}

sub ListUser{
	$userwork = "showusers\n";
	SetCommands("");
}


sub SetCommands(){
	genCommFiles();
	open (CFILE, ">$commands_file");
	print CFILE $userwork;
	print CFILE "exit";
	close (CFILE);
	UserAdminWork("", $_[0]);
}

sub genCommFiles {
	$commands_file = rand(100);
	$commands_file =~ s/\.//g;  #remove '.'
	$results_file = rand(100);
	$results_file =~ s/\.//g;  #remove '.'
	print("CON: Temp Files: commands=$commands_file, results=$results_file\n");
}

sub UserAdminWork{
	my($name, $type) = @_;

	if (length($name) > 0) {
		system("echo \"get_type\n$name\" | /usr/bin/perl $Bin/user_admin.pl stdin") >> 8;
	}
	my $useraction = system("echo \"clogin\n$cuser\n$cpass\n$commands_file\n$results_file\" | /usr/bin/perl $Bin/user_admin.pl stdin") >> 8;
	if ($useraction != 7) {  # the 7 meant for "Bad Password"
		if (-e $results_file) {
			open RFILE, "$results_file" or die $!; 
			my @results = <RFILE>;
			$answer = "";
			if ($type eq "NEWPW") {
				$answer = "Old password does not match.\n";
				$answer = "\n******* Password has changed.  Please logout and log back in.\n";
				if ($results[0] =~ /not match/) {
					$answer = "Old password does not match.\n";
				}
			}
			else {
				for $i(0 .. $#results){
					$results[$i] =~ s/Type = A/Type = Admin/;
					$results[$i] =~ s/Type = C/Type = Control/;
					$results[$i] =~ s/Type = M/Type = Monitor/;
					$results[$i] =~ s/Type = G/Type = Guest/;
					$answer = $answer . "$results[$i]";
				}
			}
		}
	}

	$name = "";
	$pass = "";
	$pass2 = "";
	$oldpass = "";
	$newname = "";
	$confirm = "";
	unlink "$commands_file";
	unlink "$results_file";
}

my @sensorFlag;
my @sensorFlagSave;
sub RemoveSensor {
	my $subwin1 = $window->Toplevel;
	my $y = 25 * $#sensorlist + 80;
	$subwin1->geometry("550x$y");
	$subwin1->title("Delete Sensor");
	if ($#sensorlist >= 0) {
		$subwin1->Label(-text => "Sensor List")->pack();
		for $i(0 .. $#sensorlist){
			my $msg = buildSensorMsg($i, 1);
			$sensorFlag[$i] = 0;
			$sensorFlagSave[$i] = $sensorFlag[$i];
			$subwin1->Checkbutton(-text => $msg, -variable => \$sensorFlag[$i])->pack(-anchor => 'w');
		}
		$subwin1->Button(-text=> "OK", -command => sub{RemoveSensorWork(1); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
	}
	else {
		$subwin1->Label(-text => "Sensor List Empty")->pack();
		$subwin1->Button(-text=> "OK", -command => sub{RemoveSensorWork(0); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
	}
}

sub EnableSensor {
	my $subwin1 = $window->Toplevel;
	my $y = 25 * $#sensorlist + 80;
	$subwin1->geometry("430x$y");
	$subwin1->title("Enable Sensor");
	if ($#sensorlist >= 0) {
		$subwin1->Label(-text => "Sensor List")->pack();
		for $i(0 .. $#sensorlist){
			my $msg = buildSensorMsg($i, 0);
			$sensorFlag[$i] = checkSensorMode($i);
			$sensorFlagSave[$i] = $sensorFlag[$i];
			$subwin1->Checkbutton(-text => $msg, -variable => \$sensorFlag[$i])->pack(-anchor => 'w');
		}
		$subwin1->Button(-text=> "OK", -command => sub{EnableSensorWork(1); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
	}
	else {
		$subwin1->Label(-text => "Sensor List Empty")->pack();
		$subwin1->Button(-text=> "OK", -command => sub{EnableSensorWork(0); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
	}
}

sub checkSensorMode {
	my($i) = @_;
	if ($sensorlist[$i]{Mode} eq "Enable") {
		return(1);
	}
	return(0);
}

sub buildSensorMsg {
	my($i, $flag) = @_;

	my $val = $sensorlist[$i]{SetVal};
	if (length($val) <= 0) {
		$val = "N/A";
	}
	else {
		$val .= "(F)";
	}
	my $p = $st_port + $i;
	my $msg = "$p: ";
	$msg .= " Name = $sensorlist[$i]{Name}";
	$msg .= ",\tType = $sensorlist[$i]{Type}";
	$msg .= ",\tSetVal = $val";
	if ($flag == 1) {
		$msg .= ",\tMode = $sensorlist[$i]{Mode}";
	}

	return($msg);
}

sub RemoveSensorWork {
	my($flag) = @_;

	if ($flag == 0) { return; }

	my $f = 0;
	for $i(0 .. $#sensorlist){
		if ($sensorFlag[$i] != $sensorFlagSave[$i]) {
			$f = 1;
		}
	}
	if ($f == 0) { return; }

	my $j = 0;
	for $i (0 .. $#sensorlist) {
		if ($sensorFlag[$i] eq "0") {
			for $role (keys %{$sensorlist[$i]}) {
				$locSensor[$j]{$role} = $sensorlist[$i]{$role};
				$sensorlist[$i]{$role} = undef;
			}
			$j += 1;
		}
	}

	for $i (0 .. $#locSensor) {
		for $role (keys %{$sensorlist[$i]}) {
			$sensorlist[$i]{$role} = $locSensor[$i]{$role};
			$locSensor[$i]{$role} = undef;
		}
	}
	$#sensorlist = $#locSensor;
	$#locSensor = -1;

	writeConsoleData();
	killProcD("sensor_d.pl");
	$answer = "\n****** Please re-ARM the system ******\n";
}

sub EnableSensorWork {
	my($flag) = @_;

	if ($flag == 0) { return; }

	my $f = 0;
	for $i(0 .. $#sensorlist){
		if ($sensorFlag[$i] != $sensorFlagSave[$i]) {
			$f = 1;
		}
	}
	if ($f == 0) { return; }

	for $i (0 .. $#sensorlist) {
		if ($sensorFlag[$i] eq "1") {
			$sensorlist[$i]{Mode} = "Enable";
		}
		else {
			$sensorlist[$i]{Mode} = "Disable";
		}
	}

	writeConsoleData();
	killProcD("sensor_d.pl");
	$answer = "\n****** Please re-ARM the system ******\n";
}

sub AddNeighbor{
	if ($#neighborlist > $neighbor_max) {
		$answer = "\nSorry, not able to add any more neighbor. Maximum count is $neighbor_max\n";
		return;
	}

	$name = "";
	$ip_value = "";
	my $subwin1 = $window->Toplevel;
	$subwin1->geometry("200x160");
	$subwin1->title("Add a neighbor monitoring");
	$subwin1->Label(-text => "Name")->pack();
	$subwin1->Entry(-textvariable => \$name)->pack();
	$subwin1->Label(-text => "IP Address (n.n.n.n)")->pack();
	$ip_value = "";
	$subwin1->Entry(-textvariable => \$ip_value)->pack();

	$confirm = "0";
	$subwin1->Checkbutton(-text => "Confirm Enable", -variable => \$confirm)->pack(-anchor => 'w');

	$subwin1->Button(-text=> "OK", -command => sub{AddNeighborWork(); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
}

sub AddNeighborWork{
	my $flag = 0;

	if (!validateInput($name)) {
		$answer = "\nSorry, not a valid neighbor name string\n";
		$flag = 1;
	}

	my @ip_tok = split(/\./, $ip_value);
	if ($#ip_tok == 3) {
		for $i(0 .. $#ip_tok){
			if (!validateInputNum($ip_tok[$i], 255)) {
				$flag = 1;
			}
		}
		if ($flag == 1) {
			$answer = "\nSorry, not a valid neighbor IP address\n";
		}
	}
	else {
		$answer = "\nSorry, not a valid neighbor IP address\n";
		$flag = 1;
	}

	if ($flag == 1) {
		$name = "";
		return;
	}

	my $mode = "Enable";
	if ($confirm eq "0") {
		$mode = "Disable";
	}

	$answer = "Added neighbor \"" . $name . " " . $ip_value . "\" in \"" . $mode . "\" mode\n";
	push @neighborlist, {Mode => $mode, Name => $name, Address => $ip_value};

	writeConsoleData();
	$name = "";
}

my @neighborFlag;
sub RemoveNeighbor {
	my $subwin1 = $window->Toplevel;
	my $y = 25 * $#neighborlist + 80;
	$subwin1->geometry("550x$y");
	$subwin1->title("Delete Neighbor");
	if ($#neighborlist >= 0) {
		$subwin1->Label(-text => "Neighbor List")->pack();
		for $i(0 .. $#neighborlist){
			$neighborFlag[$i] = 0;
			my $msg = buildNeighborMsg($i, 1);
			$neighborFlag[$i] = 0;
			$subwin1->Checkbutton(-text => $msg, -variable => \$neighborFlag[$i])->pack(-anchor => 'w');
		}
		$subwin1->Button(-text=> "OK", -command => sub{RemoveNeighborWork(1); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
	}
	else {
		$subwin1->Label(-text => "Neighbor List Empty")->pack();
		$subwin1->Button(-text=> "OK", -command => sub{RemoveNeighborWork(0); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
	}
}

sub EnableNeighbor {
	my $subwin1 = $window->Toplevel;
	my $y = 25 * $#neighborlist + 80;
	$subwin1->geometry("400x$y");
	$subwin1->title("Enable Neighbor");
	if ($#neighborlist >= 0) {
		$subwin1->Label(-text => "Neighbor List")->pack();
		for $i(0 .. $#neighborlist){
			$neighborFlag[$i] = 0;
			my $msg = buildNeighborMsg($i, 0);
			$neighborFlag[$i] = checkNeighborMode($i);
			$subwin1->Checkbutton(-text => $msg, -variable => \$neighborFlag[$i])->pack(-anchor => 'w');
		}
		$subwin1->Button(-text=> "OK", -command => sub{EnableNeighborWork(1); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
	}
	else {
		$subwin1->Label(-text => "Neighbor List Empty")->pack();
		$subwin1->Button(-text=> "OK", -command => sub{EnableNeighborWork(0); $subwin1->destroy if Tk::Exists($subwin1);})->pack();
	}
}

sub checkNeighborMode {
	my($i) = @_;
	if ($neighborlist[$i]{Mode} eq "Enable") {
		return(1);
	}
	return(0);
}

sub buildNeighborMsg{
	my($i, $flag) = @_;

	my $msg = "$i: ";

	$msg .= " Name = $neighborlist[$i]{Name}";
	$msg .= ",\tAddress = $neighborlist[$i]{Address}";
	if ($flag == 1) {
		$msg .= ",\tMode = $neighborlist[$i]{Mode}";
	}
	return($msg);
}

sub RemoveNeighborWork {
	my($flag) = @_;

	if ($flag == 0) { return; }

	my $j = 0;
	for $i (0 .. $#neighborlist) {
		if ($neighborFlag[$i] eq "0") {
			for $role (keys %{$neighborlist[$i]}) {
				$locNeighbor[$j]{$role} = $neighborlist[$i]{$role};
				$neighborlist[$i]{$role} = undef;
			}
			$j += 1;
		}
	}

	for $i (0 .. $#locNeighbor) {
		for $role (keys %{$neighborlist[$i]}) {
			$neighborlist[$i]{$role} = $locNeighbor[$i]{$role};
			$locNeighbor[$i]{$role} = undef;
		}
	}
	$#neighborlist = $#locNeighbor;
	$#locNeighbor = -1;
	writeConsoleData();
}

sub EnableNeighborWork {
	my($flag) = @_;

	if ($flag == 0) { return; }

	for $i (0 .. $#neighborlist) {
		if ($neighborFlag[$i] eq "1") {
			$neighborlist[$i]{Mode} = "Enable";
		}
		else {
			$neighborlist[$i]{Mode} = "Disable";
		}
	}

	writeConsoleData();
}

sub ListSensor{
	readConsoleData();

	if ($#sensorlist < 0) {
		$answer = "Sensor List Empty\n";
		return;
	}

	$answer = "Sensor List\n";
	for $i(0 .. $#sensorlist){
		my $val = $sensorlist[$i]{SetVal};
		if (length($val) <= 0) {
			$val = "N/A";
		}
		else {
			$val .= "(F)";
		}
		my $p = $st_port + $i;
		$answer .= "$p: ";
		$answer .= " Name = $sensorlist[$i]{Name},\t";
		$answer .= " Type = $sensorlist[$i]{Type},\t";
		$answer .= " SetVal = $val,\t";
		$answer .= " Mode = $sensorlist[$i]{Mode}\n";
	}
}

sub ListNeighbor{
	readConsoleData();

	if ($#neighborlist < 0) {
		$answer = "Neighbor List Empty\n";
		return;
	}

	$answer = "Neighbor List\n";
	for $i(0 .. $#neighborlist){
		$answer .= "$i: ";
		$answer .= " Name = $neighborlist[$i]{Name},\t";
		$answer .= " Address = $neighborlist[$i]{Address},\t";
		$answer .= " Mode = $neighborlist[$i]{Mode}\n";
	}
}

sub ArmStatus {
	readConsoleData();
	SystemHealthWork(0);

	my $ln = 4;
	my $flag = 0;
	my $msg = "\n";
	foreach my $k (sort keys (%portHashSensor)) {
		my($p, $ip) = split(/,/, $k);
		$ln -= 1;
		if ($ln <= 0) {
			$msg .= "\n";
			$ln = 3;
		}
		$msg .= "\t$p($portHashSensor{$k})";
		if ($portHashSensor{$k} =~ /\_Up/) { $flag = 1; }
	}
	$ln = 3;
	$msg .= "\n";
	foreach my $k (sort keys (%portHashNeighbor)) {
		my($p, $ip) = split(/,/, $k);
		$ln -= 1;
		if ($ln <= 0) {
			$msg .= "\n";
			$ln = 2;
		}
		$msg .= "\t$p($portHashNeighbor{$k})";
		if ($portHashNeighbor{$k} =~ /\_Up/) { $flag = 1; }
	}
	if (length($msg) > 0) {
		armMsg($flag);
		$answer = $arm_status . "Simulator Port List: " . $msg;
	}
	else {
		armMsg(-1);
		$answer = $arm_status;
	}
}

sub ArmSystem{
	readConsoleData();

	if ($#sensorlist < 0) {
		$answer = "Sensor List Empty\n";
		return;
	}

	#Start/Restart the sensor daemon
	killProcD("sensor_d.pl");
	for $i(0 .. $#sensorlist){
		if ($sensorlist[$i]{Mode} eq "Enable") {
			my $p = $st_port + $i;
			system("echo \"$p\n$resetpw\n$resetpw\" | /usr/bin/perl $Bin/sensor_d.pl stdin &");
		}
	}
	system("echo \"$pub_port\n$pub_resetpw\n$pub_resetpw\" | /usr/bin/perl $Bin/sensor_d.pl stdin &");
	system("/usr/bin/perl $Bin/Monitor.pl &");

	sleep(1);
	ArmStatus();
}


sub DisarmSystem{
	killProcD("sensor_d.pl");
	killProcD("Monitor.pl");
	killProcD("alarm_on.pl");
	ArmStatus();
}

sub SystemHealth {
	SystemHealthWork(1);
}

sub SystemHealthWork {
	my($flag) = @_;

	my $hmsg = "";
	$#portHashSensor = -1;
	$#portHashNeighbor = -1;

	readConsoleData();

	if ($#sensorlist < 0) {
		$hmsg = "Sensor List Empty\n";
		return;
	}

	$hmsg = "Sensor Health:\n";
	for $i(0 .. $#sensorlist) {
		my $p = $st_port+$i;
		$portHashSensor{"$p,local"} = "_Skip";
		if ($sensorlist[$i]{Mode} eq "Enable") {
			$portHashSensor{"$p,local"} = "_Up";
			my $socket = connectSensorD($host, $p);
			my($rc, $health) = getHealthData($socket, $host, $st_port+$i, $resetpw);
			if ($rc == 0) {
				$health = $unknown;
				$portHashSensor{"$p,local"} = "_Down";
			}

			$val = $sensorlist[$i]{SetVal};
			if (length($val) <= 0) {
				$val = "N/A";
			}
			else {
				$val .= "(F)";
			}
			$hmsg .= "$p: ";
			$hmsg .= " Name = $sensorlist[$i]{Name},\t";
			$hmsg .= " Type = $sensorlist[$i]{Type},\t";
			$hmsg .= " SelVal = $val,\t";
			$hmsg .= " Health: $health\n"
		}
	}

	$hmsg .= "\nNeighbor Health:\n";
	for $i(0 .. $#neighborlist) {
		my $h = $neighborlist[$i]{Address};
		$portHashNeighbor{"$pub_port,$h"} = "Neighbor $h _Skip";
		if ($neighborlist[$i]{Mode} eq "Enable") {
			my $socket = connectSensorD($h, $pub_port);
			$portHashNeighbor{"$pub_port,$h"} = "Neighbor $h _Up";
			my($rc, $health) = getHealthData($socket, $h, $pub_port, $pub_resetpw);
			if ($rc == 0) {
				$health = $unknown;
				$portHashNeighbor{"$pub_port,$h"} = "Neighbor $h _Down";
			}

			$hmsg .= "$pub_port: ";
			$hmsg .= " Name = $neighborlist[$i]{Name},\t";
			$hmsg .= " Address = $neighborlist[$i]{Address},\t";
			$hmsg .= " Health: $health\n"
		}
	}

	if ($flag == 1) { $answer = $hmsg; }
}

sub connectSensorD {
	my($host, $port) = @_;

	my $socket = new IO::Socket::INET (
		PeerHost => $host,
		PeerPort => $port,
		Proto => "tcp",
	);
	if ($socket == 0) {
		print "CON: Socket creation error: $!\n";
		return(0);
	};

	$socket->autoflush(1);
	print("CON: TCP connection successful: $host port $port\n");
	return($socket);
}

sub getHealthData {
	my($socket, $host, $port, $passwd) = @_;

	if ($socket == 0) {
		return(0, "");
	}

	#Request the nonce
	print "CON: Requesting nonce...";
	print $socket "REQ_NONCE\n";

	#Receive the nonce
	my $nonce = undef;
	$socket->recv($nonce, 1024);
	chomp $nonce;

	if ($nonce =~ /^NONCE ([a-f0-9]+)$/) {
		print "CON: $nonce\n";
		$nonce = $1;
	}
	else {
		print("CON: Problem, the sensor would not provide nonce.\n");
		return(0, "");
	}

	# Build our nonce, encode authentication, then request status.
	print "CON: Requesting status... ";
	my $cnonce = join '', gettimeofday();

	#my $auth_hash = md5_hex("REQ_STATUS user".$nonce.$cnonce.$passwd);
	my $auth_hash = sha256_hex("REQ_STATUS user".$nonce.$cnonce.$passwd);

	# Take our selected action - get status
	print "CON: REQ_STATUS,user,$cnonce,$auth_hash\n";
	print $socket "REQ_STATUS,user,$cnonce,$auth_hash\n";

	# Receive the response, the actual status message, why we're here, etc.
	my $response = undef;
	$socket->recv($response, 1024);
	chomp $response;
	if ($response =~ /^STATUS/) {
		my($cmd,$nonce,$hash) = split(/,/, $response);

		#my $auth_hash = md5_hex($cmd.$nonce.$cnonce.$passwd);
		my $auth_hash = sha256_hex($cmd.$nonce.$cnonce.$passwd);

		print "CON: $response\n";
		print "CON: SENSOR $host:$port cmd=$cmd, hash=$hash(expect:$auth_hash)\n";
		if ($auth_hash eq $hash) {
			$cmd =~ s/^STATUS //;
			$cmd =~ s/\(\)//g;
			return(1, $cmd);
		}
	}
	print "CON: Problem, invalid response.\n";
	return(0,"");
}

sub armMsg {
	my($flag) = @_;

	if ($flag == 1) {
		$arm_status  = "     *****************************\n\n";
		$arm_status .= "           SYSTEM ARMED\n\n";
		$arm_status .= "     *****************************\n\n";
		$arm_status .= "All sensor simulator daemons are running...\n\n";
	}
	elsif ($flag == 0) {
		$arm_status  = "     ********************************\n\n";
		$arm_status .= "           SYSTEM DISARMED\n\n";
		$arm_status .= "     ********************************\n\n";
		$arm_status .= "All sensor simulator daemons have stopped.\n\n";
	}
	else {
		$arm_status = "\nUnknown at this time.\n";
	}
}

sub killProcD {
	my($pname) = @_;

	my @p = `/bin/ps -ef |grep $pname |grep -v grep`;

	foreach my $m (@p) {
		$m =~ s/( +)/ /g;  #combine multiple spaces to one
		my @tok = split(/ /, $m);
		my $rc = `kill -9 $tok[1]`;
	}

	armMsg(0);
}

sub fin{
$answer = "Goodbye\n";
$window->destroy;

}

sub init_datalist {
	my $i = 0;

	$sensorlist[0] = ({Mode => "Enable", Name => "FrontDoor", Type => "Door", SetVal => ""});
	for ($i = 1;  $i < $mx_port; $i++) {
		$sensorlist[$i] = ({Mode => "Disable", Name => "", Type => "", SetVal => ""});
	}
	$#sensorlist = $i - 1;

	$neighborlist[0] = ({Mode => "Enable", Name => "Self", Address => "127.0.0.1"});
	$#neighborlist = 0;
}

sub BEGIN {
	$answer = "";
	$sensor= "";
	$user= "";
	$pass= "";
	$mx_port = 24;
	@sensorlist = qw();
	@neighborlist = qw();

	init_datalist();
	if (!readConsoleData()) {
		init_datalist();
		writeConsoleData();
	}
}

